package net.oschina.allchat.message.processing.json.parsers.core;

import java.util.List;

import net.oschina.allchat.message.processing.Attribute;
import net.oschina.allchat.message.processing.ElementParserAdaptor;
import net.oschina.allchat.message.processing.IElementParser;
import net.oschina.allchat.message.processing.IParser;
import net.oschina.allchat.message.processing.IParsingContext;
import net.oschina.allchat.protocol.core.ChatId;
import net.oschina.allchat.protocol.core.LangText;
import net.oschina.allchat.protocol.core.Protocol;
import net.oschina.allchat.protocol.core.ProtocolException;
import net.oschina.allchat.protocol.core.stanza.Stanza;
import net.oschina.allchat.protocol.core.stanza.error.BadRequest;

public abstract class StanzaParser<T extends Stanza> implements IParser<T> {
	private static final String VALUE_TYPE_ERROR = "error";
	private static final String PATH_ROOT = "/";
	private static final String ATTRIBUTE_NAME_ID = "id";
	private static final String ATTRIBUTE_NAME_TYPE = "type";
	private static final String ATTRIBUTE_NAME_TO = "to";
	private static final String ATTRIBUTE_NAME_FROM = "from";
	
	public static final Object KEY_ERROR = new Object();

	@Override
	public IElementParser<T> getElementParser(String parsingPath) {
		if (PATH_ROOT.equals(parsingPath)) {
			return new ElementParserAdaptor<T>() {
				@Override
				public void processAttributes(IParsingContext<T> context, List<Attribute<?>> attributes) {
					T stanza = (T) context.getObject();
					boolean isError = false;
					for (Attribute<?> attribute : attributes) {
						String prefix = attribute.getPrefix();
						String localName = attribute.getLocalName();
						
						if (prefix != null) {
							if  (LangText.PREFIX_LANG_TEXT.equals(prefix) &&
									LangText.LOCAL_NAME_LANG_TEXT.equals(localName)) {
								stanza.setLang(attribute.stringValue());
							} else {
								throw new ProtocolException(new BadRequest(String.format(
										"unknown stanza attribute name '%s:%s'.", prefix, localName)));
							}
						} else if (ATTRIBUTE_NAME_FROM.equals(localName)) {
							stanza.setFrom(parseChatId(attribute.stringValue()));
						} else if (ATTRIBUTE_NAME_TO.equals(localName)) {
							stanza.setTo(parseChatId(attribute.stringValue()));
						} else if (ATTRIBUTE_NAME_TYPE.equals(localName)) {
							String sType = attribute.stringValue();
							if (VALUE_TYPE_ERROR.equals(sType)) {
								isError = true;
								continue;
							}
							
							processType(context, sType);
						} else if (ATTRIBUTE_NAME_ID.equals(localName)) {
							stanza.setId(attribute.stringValue());
						} else {
							throw new ProtocolException(new BadRequest(String.format(
									"invalid stanza attribute '%s:%s'.", prefix, localName)));
						}
					}
					
					if (isError) {
						context.setAttribute(KEY_ERROR, KEY_ERROR);
					}
				}
			};
		}
		
		return doGetElementParser(parsingPath);
	}
	
	private ChatId parseChatId(String value) {
		return ChatId.parse(value);
	}
	
	@Override
	public void processEmbeddedObject(IParsingContext<T> context, Protocol protocol, Object embedded) {
		((Stanza) context.getObject()).getObjects().add(embedded);
		((Stanza) context.getObject()).addObjectProtocol(embedded.getClass(), protocol);
	}
	
	protected abstract IElementParser<T> doGetElementParser(String parsingPath);
	protected abstract void processType(IParsingContext<T> context, String value);
}
